home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 November / Macworld (1999-11).dmg / Updaters / WhiteCap 3.0.4 / WhiteCap Source.sit / WhiteCap Source / Common / io / ResourceFile.cpp < prev    next >
C/C++ Source or Header  |  1999-07-13  |  13KB  |  555 lines

  1.  
  2. #include "ResourceFile.h"
  3.  
  4. #include "EgOSUtils.h"
  5. #include "CEgFileSpec.h"
  6.  
  7.  
  8. ResourceFile::ResourceFile() :
  9.     mFile( false ) {
  10.  
  11.     mLargestSerialNum = cRezBaseID;
  12.     mHead = NULL;
  13. }
  14.  
  15.  
  16. ResourceFile::~ResourceFile() {
  17.  
  18.     deleteRezRefs();
  19. }
  20.  
  21.  
  22.  
  23. CEgErr ResourceFile::open( CEgFileSpec* inSpec )  {
  24.     deleteRezRefs();
  25.     
  26.     mFile.open( inSpec );    
  27.     
  28.     if ( mFile.noErr() ) {
  29.         if ( mFile.size() > cMasterJumpPos ) {                        // If opening existing
  30.             mFile.seek( 8 );
  31.             mLargestSerialNum = mFile.GetLong();
  32.             readRezRefs();    }
  33.         else {                                                        // If making new
  34.             mFile.seek( 0 );
  35.             mFile.PutLong( cCurFileVersion );                        // Resource File version number
  36.             mFile.PutLong( cCurFileVersion );                        // Resource File version number
  37.             mLargestSerialNum = cRezBaseID;
  38.             mFile.PutLong( mLargestSerialNum );                        // Largest serial num in use
  39.             for ( int i = 0; i < cMasterJumpPos; i++ ) 
  40.                 mFile.PutByte( 0 );
  41.             mFile.flush();
  42.         }
  43.     }
  44.     
  45.     return mFile;                                                    // Return any err that occured
  46. }
  47.  
  48.  
  49. RezRefNum ResourceFile::IssueNewRefNum() {
  50.     mLargestSerialNum++;
  51.     mFile.seek( 8 );
  52.     mFile.PutLong( mLargestSerialNum ); 
  53.     return mLargestSerialNum;
  54. }
  55.  
  56.  
  57. void ResourceFile::readRezRefs() {
  58.     long            nextPos, thisPos;
  59.     RezT*            rezPtr;
  60.     long            fileSize = mFile.size();
  61.     
  62.     deleteRezRefs();
  63.     
  64.     mFile.seek( cMasterJumpPos );
  65.     nextPos = mFile.GetLong();
  66.     
  67.     while ( mFile.noErr() && nextPos > 0 ) {
  68.         thisPos = nextPos;
  69.         mFile.seek( thisPos );
  70.         if ( mFile.GetLong() == cRezFlag && nextPos < fileSize ) {
  71.             nextPos = mFile.GetLong();
  72.             
  73.             rezPtr = new RezT;
  74.             rezPtr -> mPos                 = thisPos;
  75.             rezPtr -> mNext             = mHead;
  76.             rezPtr -> mSerialNum        = mFile.GetLong();
  77.             rezPtr -> mPhysicalSize        = mFile.GetLong();
  78.             rezPtr -> mLogicalSize        = mFile.GetLong(); 
  79.             
  80.             if ( mFile.noErr() ) {
  81.                 if ( rezPtr -> mSerialNum > mLargestSerialNum )
  82.                     mLargestSerialNum = rezPtr -> mSerialNum;
  83.                 
  84.                 mHead = rezPtr; }
  85.             else {
  86.                 mFile.throwErr( cNoErr );
  87.                 mFile.throwErr( cResHeaderCorrupt );
  88.                 delete rezPtr;                            // Abort new ref
  89.             } }
  90.         else {
  91.             mFile.throwErr( cNoErr );
  92.             mFile.throwErr( cResHeaderCorrupt );
  93.         }
  94.             
  95.         EgOSUtils::SpinCursor();
  96.     }
  97.     
  98. }
  99.  
  100.  
  101.  
  102.  
  103.  
  104. void ResourceFile::deleteRezRefs() {
  105.     RezT*     nextPtr, *rezPtr = mHead;
  106.     
  107.     while ( rezPtr ) {
  108.         nextPtr = rezPtr -> mNext;
  109.         delete rezPtr;
  110.         rezPtr = nextPtr;
  111.     }
  112.     mHead = NULL;
  113. }
  114.  
  115.  
  116.  
  117. RezT* ResourceFile::FetchRez( RezRefNum inSerialNum, RezT** outPrevRez, long inVersNum ) {
  118.     RezT*    prevPtr    = NULL;
  119.     RezT*     retPtr    = NULL;
  120.     RezT*    rezPtr;
  121.  
  122.     if ( inVersNum < 1 )
  123.         inVersNum = 1;
  124.         
  125.     if ( mHead ) {
  126.         if ( mHead -> mSerialNum == inSerialNum )
  127.             inVersNum--;
  128.         if ( inVersNum == 0 )                                    // If we found it
  129.             retPtr = mHead;
  130.         else if ( mHead -> mNext ) {
  131.             prevPtr = mHead;
  132.             rezPtr = mHead -> mNext;
  133.             while ( rezPtr && ! retPtr  ) {
  134.                 if ( rezPtr -> mSerialNum == inSerialNum )        // Newest ones apprear first
  135.                     inVersNum--;
  136.                 if ( inVersNum == 0 )                            // If we found it
  137.                     retPtr = rezPtr;
  138.                 else {
  139.                     prevPtr    = rezPtr;
  140.                     rezPtr    = rezPtr -> mNext;
  141.                 }
  142.             } 
  143.         }
  144.     }
  145.     
  146.     if ( outPrevRez )
  147.         *outPrevRez = prevPtr;
  148.         
  149.     return retPtr;
  150. }
  151.  
  152.  
  153.  
  154. void ResourceFile::ReleaseExtraVersions( RezRefNum inSerialNum, long inMaxNum ) {
  155.     RezT* rezPtr, *prevPtr;
  156.     
  157.     do {
  158.         rezPtr = FetchRez( inSerialNum, &prevPtr, inMaxNum );
  159.         if ( rezPtr ) {
  160.             releaseRez( rezPtr, prevPtr );
  161.             EgOSUtils::SpinCursor();
  162.         }
  163.     } while ( rezPtr );
  164. }
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171.  
  172.  
  173. CEgErr ResourceFile::GetResInfo( RezRefNum inRefNum, long* outSize ) {
  174.     RezT*    rezPtr    = FetchRez( inRefNum, NULL, 1 );    
  175.         
  176.     if ( rezPtr )  {
  177.         if ( outSize )
  178.             *outSize = rezPtr -> mLogicalSize;
  179.         return cNoErr; }
  180.     else if ( inRefNum <= 0 )
  181.         return cResourceInvalid;
  182.     else {
  183.         if ( outSize )
  184.             *outSize = 0;
  185.         return cResourceNotFound;
  186.     }
  187. }
  188.  
  189.  
  190. CEgErr ResourceFile::GetRes( RezRefNum inRefNum, UtilStr& outData ) {
  191.     RezT*    rezPtr    = FetchRez( inRefNum, NULL, 1 );    
  192.         
  193.     mFile.throwErr( cNoErr );
  194.     
  195.     if ( rezPtr ) {
  196.         mFile.seek( rezPtr -> mPos + cRezHeaderSize );
  197.         outData.Assign( mFile, rezPtr -> mLogicalSize ); 
  198.         return mFile; }
  199.     else if ( inRefNum <= 0 )
  200.         return cResourceInvalid;
  201.     else
  202.         return cResourceNotFound;
  203. }
  204.  
  205.  
  206.  
  207. CEgErr ResourceFile::GetRes( RezRefNum inRefNum, CEgIStream& outStream ) {
  208.     RezT*    rezPtr    = FetchRez( inRefNum, NULL, 1 );    
  209.     
  210.     mFile.throwErr( cNoErr );
  211.     
  212.     if ( rezPtr ) {
  213.         mFile.seek( rezPtr -> mPos + cRezHeaderSize );
  214.         outStream.Assign( &mFile, rezPtr -> mLogicalSize );
  215.         return mFile; }
  216.     else if ( inRefNum <= 0 )
  217.         return cResourceInvalid;
  218.     else
  219.         return cResourceNotFound;
  220. }
  221.  
  222.  
  223.  
  224. CEgErr ResourceFile::SetRes( RezRefNum inRefNum, const UtilStr* inData, RezWriteMode inMode, unsigned short inSlop ) {
  225.     void*        srce = NULL;
  226.     long        len = 0;
  227.     
  228.     if ( inData ) {
  229.         srce = inData -> getCStr();
  230.         len = inData -> length();
  231.     }
  232.         
  233.     return SetRes( inRefNum, srce, len, inMode, inSlop );
  234. }
  235.  
  236.  
  237.  
  238. CEgErr ResourceFile::SetRes( RezRefNum inRefNum, const void* inPtr, unsigned long inSize, RezWriteMode inMode, unsigned short inSlop ) {
  239.     RezT*    rezPtr = ( inMode == cReplaceMode ) ? FetchRez( inRefNum, NULL, 1 ) : NULL;
  240.     CEgErr    retErr;
  241.     bool    addNew = true;
  242.  
  243.     
  244.     mFile.throwErr( cNoErr );
  245.     
  246.     if ( inRefNum <= 0 || inRefNum > mLargestSerialNum )
  247.         return cResourceInvalid;
  248.     else {
  249.         if ( rezPtr ) {
  250.             if ( rezPtr -> mPhysicalSize >= inSize ) {
  251.                 addNew = false;
  252.                 mFile.seek( rezPtr -> mPos + cRezHeaderSize - 4 );        // Seek LogicalSize Pos.  WARNING: Subject to change on header change
  253.                 rezPtr -> mLogicalSize = inSize; 
  254.             }
  255.         }
  256.         
  257.         if ( addNew ) {
  258.             mFile.seekEnd();
  259.             
  260.             rezPtr = new RezT;
  261.             
  262.             rezPtr -> mSerialNum        = inRefNum;
  263.             rezPtr -> mLogicalSize        = inSize;
  264.             rezPtr -> mPhysicalSize        = inSize + inSlop;
  265.             rezPtr -> mPos                = mFile.tell();
  266.             rezPtr -> mNext                = mHead;
  267.             
  268.             mFile.PutLong( cRezFlag );
  269.             mFile.PutLong( 0 );
  270.             mFile.PutLong( rezPtr -> mSerialNum );
  271.             mFile.PutLong( rezPtr -> mPhysicalSize );
  272.         }
  273.         
  274.         mFile.PutLong( rezPtr -> mLogicalSize );
  275.         mFile.PutBlock( inPtr, rezPtr -> mLogicalSize );
  276.         mFile.CEgOStream::skip( rezPtr -> mPhysicalSize - rezPtr -> mLogicalSize );
  277.         mFile.flush();                                    // We want to make sure we catch any disk errors
  278.         
  279.         if ( mFile.noErr() ) {                            // If successful
  280.             if ( addNew ) {
  281.                 if ( mHead )
  282.                     mFile.seek( mHead -> mPos + 4 );    // Move to NextRezFilePtr
  283.                 else
  284.                     mFile.seek( cMasterJumpPos );
  285.                 mFile.PutLong( rezPtr -> mPos );         // Finish the file link
  286.                 mFile.flush();
  287.                 mHead = rezPtr;                            // Finish Inserting the new Rez item in the list
  288.             } 
  289.         }
  290.         
  291.         if ( mFile.noErr() ) {
  292.             if ( inMode == cReplaceMode || inMode == cSafeReplaceMode )
  293.                 ReleaseExtraVersions( inRefNum, 3 );
  294.         }
  295.         
  296.         return mFile;                                    // Return any err
  297.     }
  298. }
  299.  
  300.  
  301.  
  302. void ResourceFile::ReleaseRes( RezRefNum inRefNum ) {
  303.     CEgErr    delErr;
  304.     
  305.     do {
  306.         delErr = RevertRes( inRefNum );
  307.     } while ( delErr.noErr() );
  308. }
  309.  
  310.  
  311.  
  312. CEgErr ResourceFile::RevertRes( RezRefNum inRefNum ) {
  313.     RezT*            prevPtr;
  314.     RezT*            rezPtr = FetchRez( inRefNum, &prevPtr, 1 );
  315.     
  316.     return releaseRez( rezPtr, prevPtr );
  317. }
  318.  
  319.  
  320.  
  321.  
  322. CEgErr ResourceFile::releaseRez( RezT* rezPtr, RezT* prevPtr ) {
  323.     unsigned long    nextRezPos;
  324.     CEgErr            retErr;
  325.     
  326.     mFile.throwErr( cNoErr );
  327.     
  328.     if ( rezPtr ) {
  329.         mFile.seek( rezPtr -> mNext ? rezPtr -> mNext -> mPos + 4: cMasterJumpPos );
  330.         nextRezPos = prevPtr ? prevPtr -> mPos: 0;                            // We denote end of link as 0
  331.         if ( mFile.noErr() )
  332.             mFile.PutLong( nextRezPos );
  333.         if ( mFile.noErr() ) {
  334.             if ( prevPtr )
  335.                 prevPtr -> mNext = rezPtr -> mNext;
  336.             else
  337.                 mHead = rezPtr -> mNext;
  338.             delete rezPtr; }
  339.         else
  340.             retErr = mFile; }
  341.     else
  342.         retErr.throwErr( cResourceNotFound );
  343.     
  344.     return retErr;
  345. }
  346.  
  347.  
  348.  
  349.  
  350.         
  351. int    ResourceFile::CalcPercentGarbage() {
  352.     RezT*        rezPtr    = mHead;
  353.     long        used     = cMasterJumpPos + 200;
  354.     long        percent;
  355.     
  356.     while ( rezPtr ) {                                            // Newest come first
  357.         used += rezPtr -> mPhysicalSize + cRezHeaderSize;
  358.         rezPtr = rezPtr -> mNext;
  359.     }
  360.     
  361.     mFile.throwErr( cNoErr );
  362.     
  363.     percent = 100 - (100 * used) / mFile.size();
  364.  
  365.     if ( percent < 0 )
  366.         percent = 0;
  367.         
  368.     if ( mFile.noErr() )
  369.         return percent;
  370.     else
  371.         return 0;
  372. }
  373.  
  374.  
  375.  
  376.  
  377. CEgErr ResourceFile::Duplicate( CEgFileSpec& inDestSpec ) {
  378.     CEgErr            err;
  379.     RezT*            rezPtr = mHead;
  380.     UtilStr        theData;
  381.     unsigned short    slop;
  382.     ResourceFile    newRF;
  383.     
  384.     err = newRF.open( &inDestSpec );
  385.     
  386.     while ( rezPtr && err.noErr() ) {
  387.         if ( ! newRF.FetchRez( rezPtr -> mSerialNum, NULL, 1 ) ) {            // Don't write old resource versions
  388.             err = GetRes( rezPtr -> mSerialNum, theData );
  389.             if ( err.noErr() ) {
  390.                 slop = rezPtr -> mPhysicalSize - rezPtr -> mLogicalSize;
  391.                 
  392.                 // Maintain what serial nums have been issued
  393.                 if ( newRF.mLargestSerialNum < rezPtr -> mSerialNum )
  394.                     newRF.mLargestSerialNum = rezPtr -> mSerialNum;
  395.                     
  396.                 err = newRF.SetRes( rezPtr -> mSerialNum, &theData, cReplaceMode, slop );
  397.             }
  398.             EgOSUtils::SpinCursor();
  399.         }
  400.             
  401.         rezPtr = rezPtr -> mNext;
  402.     }
  403.     
  404.     // Flush what serial num we're up to to disk
  405.     if ( err.noErr() )
  406.         newRF.IssueNewRefNum();
  407.     
  408.     return err;
  409. }
  410.  
  411.  
  412.  
  413.  
  414. void ResourceFile::Search( UtilStr& inSearchStr, void* inProcArg, bool inCaseSensitive, AddRefHitFcnT inAddHitFcn ) {
  415.     SearchArgT    info = { this, inProcArg, inAddHitFcn };
  416.     
  417.     mFile.throwErr( cNoErr );
  418.     mFile.Search( inSearchStr, &info, inCaseSensitive, sFilePosHitFcn );
  419. }
  420.  
  421.  
  422. long ResourceFile::sFilePosHitFcn( void* inProcArg, long inFilePos ) {
  423.     SearchArgT*    info = (SearchArgT*) inProcArg;
  424.     
  425.     return info -> thisPtr -> filePosHitFcn( info -> refHitProcArg, inFilePos, info -> addRefHitFcn );
  426. }
  427.  
  428.     
  429. long ResourceFile::filePosHitFcn( void* inProcArg, long inFilePos, AddRefHitFcnT inRefHitFcn ) {
  430.     RezT*        rezPtr        = mHead;
  431.     RezT*        hitPtr        = NULL;
  432.     long        skipBytes    = 0;
  433.     
  434.     while ( rezPtr && ! hitPtr ) {
  435.         if ( inFilePos > rezPtr -> mPos + cRezHeaderSize ) 
  436.             if ( inFilePos < rezPtr -> mPos + rezPtr -> mLogicalSize ) 
  437.                 hitPtr = rezPtr;
  438.         rezPtr = rezPtr -> mNext;
  439.     }
  440.     
  441.     if ( hitPtr ) {
  442.         skipBytes = hitPtr -> mPhysicalSize - ( inFilePos - hitPtr -> mPos );
  443.         rezPtr = FetchRez( hitPtr -> mSerialNum, NULL, 1 );                
  444.         if ( rezPtr == hitPtr ) {                                             // Make sure the hit was in a non-backup Rez
  445.             if ( ! inRefHitFcn( inProcArg, hitPtr -> mSerialNum ) )
  446.                 skipBytes = -1;                                                // Send flag to stop search
  447.         }
  448.     }
  449.     
  450.     return skipBytes;
  451. }
  452.  
  453.  
  454.  
  455.  
  456.  
  457. CEgErr ResourceFile::EmergencyRecover( XLongList& inSearchList ) {
  458.     CEgOStream        temp;
  459.     RFRecoverParamT    param;
  460.     long            marker = cRezFlag;
  461.     UtilStr        str( &marker, 4 );
  462.     
  463.     mFile.throwErr( cNoErr );
  464.  
  465.     // Setup the param for searching for the Rez flag marker
  466.     param.mOrigSize            = mFile.size();
  467.     param.mSearchList        = &inSearchList;
  468.     param.mSourceRF            = this;
  469.     param.mErr.throwErr( cNoErr );
  470.     
  471.     // Search this RF's file at a low level for cRezFlags, recovering data blocks
  472.     mFile.Search( str, ¶m, true, RecoverRezFcn );
  473.     
  474.     // Update to disk the highest ref num issued
  475.     IssueNewRefNum();
  476.         
  477.     return param.mErr;
  478.         
  479.     
  480.  
  481.  
  482.  
  483. long ResourceFile::RecoverRez( long inPos, XLongList& inSearchList, long inOrigSize, CEgErr& outErr ) {
  484.     RezRefNum    rezNum;
  485.     UtilStr    data;
  486.     long         physSize, logSize, pos;
  487.     
  488.     outErr.throwErr( cNoErr );
  489.     
  490.     // Skip the cRezFlag
  491.     mFile.throwErr( cNoErr );
  492.     mFile.seek( inPos + 4 );
  493.  
  494.     // If the pos of the next rez looks suspicious, then abort
  495.     pos = mFile.GetLong();
  496.     if ( pos < 0 || pos > inOrigSize )
  497.         return 4;
  498.         
  499.     // If the resource ID looks suspicious, then abort
  500.     rezNum = mFile.GetLong();
  501.     if ( rezNum < 0 || rezNum > 10000000 )
  502.         return 8;
  503.         
  504.     // If the logical or physical size look suspicious, then abort
  505.     physSize     = mFile.GetLong();
  506.     logSize        = mFile.GetLong();
  507.     if ( logSize > physSize || inPos + logSize > inOrigSize || inPos + physSize > inOrigSize || physSize < 0 || logSize < 0 )
  508.         return 16;
  509.             
  510.     // See if we're looking for this rez.  If we are, write a new copy of the rez
  511.     if ( inSearchList.FindIndexOf( rezNum ) > 0 ) {
  512.     
  513.         // Read the res's data into a buffer
  514.         mFile.Read( data, logSize );    
  515.         
  516.         if ( mFile.noErr() ) {
  517.             // Make sure we don't issue this rez again
  518.             if ( mLargestSerialNum < rezNum )
  519.                 mLargestSerialNum = rezNum;
  520.                 
  521.             // Write a fresh rez in this resource file
  522.             outErr = SetRes( rezNum, &data ); }
  523.         else
  524.             outErr.throwErr( cNoErr );
  525.     }
  526.     
  527.     // This is the offset after this rez we should resume looking...
  528.     return physSize + 16;
  529. }
  530.  
  531.  
  532.  
  533.  
  534.  
  535. long ResourceFile::RecoverRezFcn( void* inProcArg, long inFilePos ) {
  536.     RFRecoverParamT*    param = (RFRecoverParamT*) inProcArg;
  537.     long skip;
  538.     
  539.     // Stop the file search if we go past the initial size of the resource file
  540.     if ( inFilePos < param -> mOrigSize ) {
  541.     
  542.         // Suck the rez info and data from the temp stream.
  543.         skip = param -> mSourceRF -> RecoverRez( inFilePos, *param -> mSearchList, param -> mOrigSize, param -> mErr );
  544.     
  545.         // If there was no errors, resume the file search
  546.         if ( param -> mErr.noErr() )
  547.             return skip;
  548.     }
  549.             
  550.             
  551.     return -1;
  552. }
  553.  
  554.